// Copyright 2017 ETH Zurich and University of Bologna.
// -- Adaptable modifications made for hbirdv2 SoC. -- 
// Copyright 2020 Nuclei System Technology, Inc.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the “License”); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

`define log2(VALUE) ((VALUE) < ( 1 ) ? 0 : (VALUE) < ( 2 ) ? 1 : (VALUE) < ( 4 ) ? 2 : (VALUE) < ( 8 ) ? 3 : (VALUE) < ( 16 )  ? 4 : (VALUE) < ( 32 )  ? 5 : (VALUE) < ( 64 )  ? 6 : (VALUE) < ( 128 ) ? 7 : (VALUE) < ( 256 ) ? 8 : (VALUE) < ( 512 ) ? 9 : (VALUE) < ( 1024 ) ? 10 : (VALUE) < ( 2048 ) ? 11 : (VALUE) < ( 4096 ) ? 12 : (VALUE) < ( 8192 ) ? 13 : (VALUE) < ( 16384 ) ? 14 : (VALUE) < ( 32768 ) ? 15 : (VALUE) < ( 65536 ) ? 16 : (VALUE) < ( 131072 ) ? 17 : (VALUE) < ( 262144 ) ? 18 : (VALUE) < ( 524288 ) ? 19 : (VALUE) < ( 1048576 ) ? 20 : (VALUE) < ( 1048576 * 2 ) ? 21 : (VALUE) < ( 1048576 * 4 ) ? 22 : (VALUE) < ( 1048576 * 8 ) ? 23 : (VALUE) < ( 1048576 * 16 ) ? 24 : 25)

`define REG_STATUS 4'b0000 // BASEREG + 0x00
`define REG_CLKDIV 4'b0001 // BASEREG + 0x04
`define REG_SPICMD 4'b0010 // BASEREG + 0x08
`define REG_SPIADR 4'b0011 // BASEREG + 0x0C
`define REG_SPILEN 4'b0100 // BASEREG + 0x10
`define REG_SPIDUM 4'b0101 // BASEREG + 0x14
`define REG_TXFIFO 4'b0110 // BASEREG + 0x18
`define REG_RXFIFO 4'b1000 // BASEREG + 0x20
`define REG_INTCFG 4'b1001 // BASEREG + 0x24
`define REG_INTSTA 4'b1010 // BASEREG + 0x28

`define SINGLE      3'b000
`define INCR        3'b001
`define WRAP4       3'b010
`define INCR4       3'b011
`define WRAP8       3'b100
`define INCR8       3'b101
`define WRAP16      3'b110
`define INCR16      3'b111

`define SPI_IDLE    7'h1
`define SPI_CMD     7'h2

`define HTRANS_IDLE      2'b00
`define HTRANS_BUSY      2'b01
`define HTRANS_NONSEQ    2'b10
`define HTRANS_SEQ       2'b11

`define FWRAP8B      2'b00
`define FWRAP16B     2'b01
`define FWRAP32B     2'b10
`define FWRAP64B     2'b11

`define HREQ8B      6'b00_1000
`define HREQ16B     6'b01_0000
`define HREQ32B     6'b01_1000
`define HREQ64B     6'b10_0000


`define SET_FLASH_WRAP_INST 32'h7700_0000
`define FAST_READ_INST      32'h0B00_0000
`define QPI_WRAP_READ_INST  32'h0C00_0000
`define SPI_ENTER_QPI_INST  32'h3800_0000
`define SET_READ_PARAMTERS  32'hC000_0000
`define PAGE_PROGRAM        32'h0200_0000


module spi_master_ahb_if
#(
    parameter BUFFER_DEPTH   = 10,
    parameter AHB_ADDR_WIDTH = 12,  //APB slaves are 4KB by default
    parameter   flash_addr_start = 32'h40000000, 
    parameter   flash_addr_end   = 32'h407fffff,
    parameter LOG_BUFFER_DEPTH = `log2(BUFFER_DEPTH)
)
(
    input  wire                        HCLK,
    input  wire                        HRESETn,
    input  wire [AHB_ADDR_WIDTH - 1:0] HADDR,
    input  wire [2:0]                  HBURST,
    input  wire [2:0]                  HSIZE,
    input  wire [2:0]                  HTRANS,
    input  wire [31:0]                 HWDATA,
    input  wire                        HWRITE,
    // input  wire                        PSEL,
    // input  wire                        PENABLE,
    output reg  [31:0]                 HRDATA,
    output wire                        HREADY,
    output wire                        HRESP,

    output reg  [7:0]                  spi_clk_div,
    output reg                         spi_clk_div_valid,
    input  wire                         spi_cpol_i,     
    input  wire                         spi_cpha_i,     
    output reg                          spi_cpol_o,
    output reg                          spi_cpha_o,
    input  wire [31:0]                 spi_status,
    output reg  [31:0]                 spi_addr,
    output reg  [5:0]                  spi_addr_len,
    output reg  [31:0]                 spi_cmd,
    output reg  [5:0]                  spi_cmd_len,
    output reg  [3:0]                  spi_csreg,
    output reg  [15:0]                 spi_data_len,
    output reg  [15:0]                 spi_dummy_rd,
    output reg  [15:0]                 spi_dummy_wr,
    output reg  [LOG_BUFFER_DEPTH:0]   spi_int_th_tx,
    output reg  [LOG_BUFFER_DEPTH:0]   spi_int_th_rx,
    output reg                         spi_int_en,
    input  wire [31:0]                 spi_int_status,
    output reg                         spi_swrst,
    output reg                         spi_rd,
    output reg                         spi_wr,
    output reg                         spi_qrd,
    output reg                         spi_qwr,
    output wire [31:0]                 spi_data_tx,
    output wire                        spi_data_tx_valid,
    input  wire                        spi_data_tx_ready,
    input  wire [31:0]                 spi_data_rx,
    input  wire                        spi_data_rx_valid,
    output wire                        spi_data_rx_ready
);
reg cpol,cpha;
reg [31:0] FSMRDATA;
reg [6:0] cur_state,nxt_state;
localparam  INIT = 7'b000_0001,
            IDLE = 7'b000_0010,
            CMD  = 7'b000_0100,
            PREC = 7'b000_1000,
            WAIT = 7'b001_0000,
            READ = 7'b010_0000,
            WRITE= 7'b100_0000;
    wire [3:0] write_address;
    wire [3:0] read_address;
    reg  [3:0] write_address_r,read_address_r;
    reg        spi_reg_wr_en,spi_reg_rd_en;

    assign write_address = HADDR[5:2];
    assign read_address  = HADDR[5:2];
    
    // assign PSLVERR = 1'b0;
    // assign PREADY  = 1'b1;
wire is_flash;
assign is_flash = HADDR >= flash_addr_start && HADDR <= flash_addr_end;

reg [31:0] begin_addr,cur_addr;
reg [5:0] burst_beats,pos_len;
reg req_is_wrap,req_is_write;
reg is_qpi;
reg is_burst;
reg [2:0] data_size;
reg [2:0] ahb_burst;
reg [1:0] flash_wrap;
reg       flash_wrap_valid;
reg       req_no_resp;
reg [31:0]wdata_buf;

always @(posedge HCLK or negedge HRESETn) begin
    if(~HRESETn) begin
        cur_state <= INIT;
    end else begin
      cur_state <= nxt_state;
    end
end

wire init_to_idle;
wire idle_to_cmd;
wire cmd_to_prec;
wire cmd_to_wait;
wire wait_to_read,wait_to_write;
wire prec_to_cmd;
wire read_to_wait,read_to_cmd,read_to_idle;
wire write_to_wait,write_to_cmd,write_to_idle;
wire is_CMD,is_PREC,is_WAIT,is_WRITE;
//for spi state
wire is_spi_idle,is_spi_cmd ;
assign is_spi_idle = spi_status[6:0] ==   `SPI_IDLE;
assign is_spi_cmd  = spi_status[6:0] ==   `SPI_CMD;



always @(posedge HCLK or negedge HRESETn) begin
    if(!HRESETn)
        is_qpi <= 1'b0;
    else if(init_to_idle)
        is_qpi <= 1'b1;
end

always@(*)begin
	case(cur_state)						//组合逻辑
										//根据当前状态、输入进行状态转换判断										
		INIT:begin
            if(init_to_idle)
                nxt_state = IDLE;
            else 
                nxt_state = INIT;
        end
        IDLE:begin				
			if(idle_to_cmd)					
				nxt_state = CMD;		
            else 
                nxt_state = IDLE;		
		end
		CMD:begin
			if(cmd_to_prec)
				nxt_state = PREC;
			else if (cmd_to_wait) 
				nxt_state = WAIT;
            else 
                nxt_state = CMD;
		end
		PREC:begin
			if (prec_to_cmd)
				nxt_state = CMD;
			else
				nxt_state = PREC;

		end
        WAIT:begin
            if (wait_to_read)
                nxt_state = READ;
            else if(wait_to_write)
                nxt_state = WRITE;
            else 
                nxt_state = WAIT;
           // else 
             //   nxt_state = IDLE;
        end
		READ:begin
            if (read_to_wait) 
                nxt_state = WAIT;
            else if(read_to_cmd)
                nxt_state = CMD;				
			else if(read_to_idle)
				nxt_state = IDLE;		
			else 
				nxt_state = READ;		
		end
        WRITE:begin
            if (write_to_wait) 
                nxt_state = WAIT;
            else if(write_to_cmd)
                nxt_state = CMD;				
			else if(write_to_idle)
				nxt_state = IDLE;		
			else 
				nxt_state = WRITE;		
        end

		default:begin					
				nxt_state = IDLE;
		end
	endcase
end
reg need_init;

assign init_to_idle = cur_state == INIT && is_spi_idle && ~need_init;


//IDLE=>CMD: spi_idle == 1 && req_valid
//req_valid ==> HTRANS== SEQ or NONSEQ
wire req_valid;
assign req_valid = HTRANS == `HTRANS_NONSEQ || HTRANS == `HTRANS_SEQ;
wire req_incr_write;
assign req_incr_write = HWRITE && HBURST != `INCR && (HBURST[0] == 1'b1 || HBURST == `SINGLE);

wire is_IDLE,idle_hready;

assign is_IDLE = cur_state == IDLE;
assign idle_hready = idle_to_cmd;

wire [4:0] ahb_burst_beats;
wire [5:0] ahb_req_bytes;
wire [8:0] ahb_req_bits;

assign ahb_burst_beats = HBURST[2:1] != 2'b0 ? 5'b00010 << {HBURST[2:1]} : 5'b1;
assign ahb_req_bytes=   burst_beats << data_size;
assign ahb_req_bits =  ahb_req_bytes<< 3;

assign idle_to_cmd = is_flash && req_valid && (!HWRITE || HWRITE && req_incr_write) && cur_state == IDLE && is_spi_idle ;

always @(posedge HCLK or negedge HRESETn ) begin
    if(!HRESETn) begin
      req_no_resp <= 1'b0;

    end
    //idle_to_cmd OR read_to_cmd OR write_to_cmd need req_valid == 1
    else if (idle_to_cmd || read_to_cmd || write_to_cmd) begin
        begin_addr  <= HADDR;
        cur_addr    <= HADDR;
        wdata_buf   <= HWRITE ? HWDATA : wdata_buf;
        burst_beats   <= ahb_burst_beats;
        req_is_write <= HWRITE;
        req_is_wrap     <= ~HBURST[0] && HBURST[2:1] != 0;
        ahb_burst   <= HBURST;
        data_size   <= HSIZE;
        is_burst    <= HBURST[2:1] != 0;
        pos_len     <= 'b1;
        req_no_resp <= 1'b1;
    end
    //for new req
    //req_valid maybe not == 1
    else if (read_to_wait && req_valid || write_to_wait )begin
        cur_addr    <= HADDR;
        req_is_write <= HWRITE;
        wdata_buf    <= HWRITE ? HWDATA : wdata_buf;
        req_is_wrap     <= ~HBURST[0] && HBURST[2:1] != 0;
        data_size   <= HSIZE;
        is_burst    <= HBURST[2:1] != 0;
        pos_len     <= pos_len + 1'b1;
        req_no_resp <= req_valid;
    end
    else if (is_WAIT && req_valid && !req_no_resp ) begin
        cur_addr    <= HADDR;
        req_is_write <= HWRITE;
        wdata_buf    <= HWRITE ? HWDATA : wdata_buf;
        req_is_wrap     <= ~HBURST[0] && HBURST[2:1] != 0;
        data_size   <= HSIZE;
        is_burst    <= HBURST[2:1] != 0;
        pos_len     <= pos_len + 1'b1;
        req_no_resp <= 1'b1;
    end
    else if(read_to_idle || write_to_idle) begin
        burst_beats   <= 'b0;
        pos_len       <= 'b0;
        req_is_wrap     <=  'b0;
        req_is_write    <= 1'b0;
        is_burst    <=  'b00;
        req_no_resp <= 1'b0;
    end
    
end
//CMD 情况//
//need  judge AHB information to 
//mismatch wrap info => exec wrap cmd
wire flash_wrap8B,flash_wrap16B,flash_wrap32B,flash_wrap64B;
wire ahb_req8B,ahb_req16B,ahb_req32B,ahb_req64B;

//check flash wrap length;
//if wrap_valid && len == ahb len not exec pre inst
//else must set flash wrap len;
//refer flash doc
assign flash_wrap8B = flash_wrap ==  `FWRAP8B;
assign flash_wrap16B = flash_wrap ==  `FWRAP16B;
assign flash_wrap32B = flash_wrap ==  `FWRAP32B;
assign flash_wrap64B = flash_wrap ==  `FWRAP64B;

assign ahb_req8B  = ahb_req_bytes ==  `HREQ8B;
assign ahb_req16B = ahb_req_bytes ==  `HREQ16B;
assign ahb_req32B = ahb_req_bytes ==  `HREQ32B;
assign ahb_req64B = ahb_req_bytes ==  `HREQ64B;

wire wrap_mismatch;
assign wrap_mismatch = ~(   flash_wrap8B  && ahb_req8B 
                    ||      flash_wrap16B && ahb_req16B
                    ||      flash_wrap32B && ahb_req32B
                    ||      flash_wrap64B && ahb_req64B); 
wire need_set_wrap; 
assign need_set_wrap = req_is_wrap && !req_is_write && (~flash_wrap_valid || wrap_mismatch);

assign cmd_to_prec = need_set_wrap;
//CMD// wirite cmd
//INCR read use FAST_READ
//WRAP read use WRAP_READ
//other use FAST_READ too. 


//////PREC//////
//exec a cmd before read flash//
//now is set wrap
//flash suupoet 8B 16B 32B 64B wrap;
//AHB HHZIE:1B 2B 4B
//AHB WRAP beats :4 8 16
//1B*4=4B;1B*8=8B;1B*16=16B;
//2B*4=8B;2B*8=16B;2B*16=32B;
//4B*4=16B;4B*8=32B;4B*16=64B;
// one mode cannot support need to process
//add todo list

// Set Burst with Wrap 77h
//need addr cycle but dont care value
//no dummy cycle
//QMODE
wire[3:0] wrap_bytes;
reg prec_tx; 
assign wrap_bytes = ( ahb_burst[2:1] << data_size[1:0] )-2;

assign is_CMD = cur_state == CMD;
assign is_PREC = cur_state == PREC;
assign is_WAIT = cur_state == WAIT;
assign is_WRITE =cur_state == WRITE;

always @(posedge HCLK or negedge HRESETn) begin
    if (~HRESETn )begin
      flash_wrap_valid <= 1'b0;
      flash_wrap  <= 2'b0;
    end
    else if (is_CMD && cmd_to_prec) begin
        flash_wrap_valid <= 1'b1;
        flash_wrap  <= wrap_bytes;
    end 
    
end


always @(posedge HCLK or negedge HRESETn) begin
    if (HRESETn == 1'b0) 
        need_init <=1'b1;
    //ensure init cmd be sent
    else if(is_spi_idle && cur_state == INIT && need_init)
        need_init <= 1'b1;
    else 
        need_init <= 1'b0;
end

reg prec_tx_valid;

always @(posedge HCLK or negedge HRESETn) begin
    if(~HRESETn) 
        prec_tx_valid <= 1'b0;

    else if (prec_tx_valid && spi_data_tx_ready)
        prec_tx_valid <= 1'b0;     
    else if (is_PREC && !spi_swrst && prec_tx) 
        prec_tx_valid <= 1'b1;  
end



always @(posedge HCLK or negedge HRESETn) begin
    if (HRESETn == 1'b0) begin
        spi_swrst         <= 1'b0;
        spi_rd            <= 1'b0;
        spi_wr            <= 1'b0;
        spi_qrd           <= 1'b0;
        spi_qwr           <= 1'b0;
        spi_clk_div_valid <= 1'b0;
        spi_clk_div       <= 'b0;
        spi_cmd           <= 'b0;
        spi_addr          <= 'b0;
        spi_cmd_len       <= 'b0;
        spi_addr_len      <= 'b0;
        spi_data_len      <= 'b0;
        spi_dummy_rd      <= 'b0;
        spi_dummy_wr      <= 'b0;
        spi_csreg         <= 'b0;
        spi_int_th_tx     <= 'b0;
        spi_int_th_rx     <= 'b0;
        spi_int_en        <= 1'b0;
        spi_cpol_o        <=1'b0;
        spi_cpha_o        <=1'b0;        
        
    end
    else if (   cur_state == INIT  && need_init ) begin
        spi_rd    <= 1'b0;
        spi_wr    <= 1'b1;
        spi_qrd   <= 1'b0;
        spi_qwr   <= 1'b0;
        spi_swrst <= 1'b0;
        spi_csreg <= 4'b1;

        spi_clk_div       <= 'b10;
        spi_clk_div_valid <= 1'b1;

        spi_cmd  <= `SPI_ENTER_QPI_INST;
        spi_addr <= 'b0;

        spi_cmd_len        <= 'h8;
        spi_addr_len       <=  'b0;
        spi_data_len[7:0]  <=  'd0;
        spi_data_len[15:8] <=  'd0;
     
        spi_dummy_rd[7:0]  <= 'b0;
        spi_dummy_rd[15:8] <= 'b0;
        spi_dummy_wr[7:0]  <= 'b0;
        spi_dummy_wr[15:8] <= 'b0;
    
        spi_int_th_tx <= 'd8;
        spi_int_th_rx <= 'd8;
        spi_int_en    <= 1'b0;
        //need write data fifo
        //need tx data

    end
    // else if(cur_state == INIT && ~need_init) begin/
    //   spi_wr <= 1'b1;
    // end
    else if(cmd_to_prec && cur_state == CMD )begin
        spi_rd    <= 1'b0;
        spi_wr    <= 1'b0;
        spi_qrd   <= 1'b0;
        spi_qwr   <= 1'b1;
        spi_swrst <= 1'b1;
        spi_csreg <= 4'b1;

        spi_clk_div       <= 'b0;
        spi_clk_div_valid <= 1'b1;

        spi_cmd  <= `SET_READ_PARAMTERS;
        spi_addr <= 'b0;

        spi_cmd_len        <= 'h8;
        spi_addr_len       <=  'd24;
        spi_data_len[7:0]  <=  'd8;
        spi_data_len[15:8] <=  'd0;
     
        spi_dummy_rd[7:0]  <= 'b0;
        spi_dummy_rd[15:8] <= 'b0;
        spi_dummy_wr[7:0]  <= 'b0;
        spi_dummy_wr[15:8] <= 'b0;
    
        spi_int_th_tx <= 'd8;
        spi_int_th_rx <= 'd8;
        spi_int_en    <= 1'b0;
        //need write data fifo
        //need tx data
    end
    else if(prec_to_cmd )begin
        spi_rd    <= 1'b0;
        spi_wr    <= 1'b0;
        spi_qrd   <= 1'b0;
        spi_qwr   <= 1'b0;
        spi_swrst <= 1'b1;
        spi_csreg <= 4'b0;

        spi_clk_div       <= 'b0;
        spi_clk_div_valid <= 1'b1;

        spi_cmd  <= 'b0;
        spi_addr <= 'b0;

        spi_cmd_len        <= 'b0;
        spi_addr_len       <=  'b0;
        spi_data_len[7:0]  <=  'b0;
        spi_data_len[15:8] <=  'd0;
     
        spi_dummy_rd[7:0]  <= 'b0;
        spi_dummy_rd[15:8] <= 'b0;
        spi_dummy_wr[7:0]  <= 'b0;
        spi_dummy_wr[15:8] <= 'b0;
    
        spi_int_th_tx <= 'd8;
        spi_int_th_rx <= 'd8;
        spi_int_en    <= 1'b0;
    end
    else if (cur_state == CMD && ~need_set_wrap)begin
    ///read command
        spi_rd    <= 1'b0;
        spi_wr    <= 1'b0;
        spi_qrd   <= ~req_is_write ;//read
        spi_qwr   <= req_is_write;
        spi_swrst <= 1'b1;
        spi_csreg <= 4'b1;

        spi_clk_div       <= 'b0;
        spi_clk_div_valid <= 1'b1;

        spi_cmd  <= req_is_write ? `PAGE_PROGRAM :  
                    req_is_wrap  ? `QPI_WRAP_READ_INST : `FAST_READ_INST;
        spi_addr <= {begin_addr[23:0],8'b0};

        spi_cmd_len        <= 'h8;
        spi_addr_len       <=  'd24;
        spi_data_len[7:0]  <=  ahb_req_bits[7:0];
        spi_data_len[15:8] <=  {7'b0,ahb_req_bits[8]};
     
        spi_dummy_rd[7:0]  <= 'd2;
        spi_dummy_rd[15:8] <= 'b0;
        spi_dummy_wr[7:0]  <= 'b0;
        spi_dummy_wr[15:8] <= 'b0;
    
        spi_int_th_tx <= 'd8;
        spi_int_th_rx <= 'd8;
        spi_int_en    <= 1'b0;
        //need write data fifo
        //need tx data
        prec_tx <= 1'b0; 
 	end 
	else if (spi_reg_wr_en) begin
            spi_swrst         <= 1'b0;
            spi_rd            <= 1'b0;
            spi_wr            <= 1'b0;
            spi_qrd           <= 1'b0;
            spi_qwr           <= 1'b0;
            spi_clk_div_valid <= 1'b0;

            case (write_address_r)
                `REG_STATUS: begin
                    spi_rd    <= HWDATA[0];
                    spi_wr    <= HWDATA[1];
                    spi_qrd   <= HWDATA[2];
                    spi_qwr   <= HWDATA[3];
                    spi_swrst <= HWDATA[4];
                    spi_csreg <= HWDATA[11:8];
                end
                `REG_CLKDIV: begin
                    spi_clk_div       <= HWDATA[7:0];
                    spi_clk_div_valid <= 1'b1;
                end
                `REG_SPICMD: spi_cmd  <= HWDATA;
                `REG_SPIADR: spi_addr <= HWDATA;
                `REG_SPILEN: begin
                    spi_cmd_len        <= HWDATA[5:0];
                    spi_addr_len       <= HWDATA[13:8];
                    spi_data_len[7:0]  <= HWDATA[23:16];
                    spi_data_len[15:8] <= HWDATA[31:24];
                end
                `REG_SPIDUM: begin
                    spi_dummy_rd[7:0]  <= HWDATA[7:0];
                    spi_dummy_rd[15:8] <= HWDATA[15:8];
                    spi_dummy_wr[7:0]  <= HWDATA[23:16];
                    spi_dummy_wr[15:8] <= HWDATA[31:24];
                end
                `REG_INTCFG: begin
                    spi_int_th_tx <= HWDATA[LOG_BUFFER_DEPTH:0];
                    spi_int_th_rx <= HWDATA[8 + LOG_BUFFER_DEPTH:8];
                    spi_int_en    <= HWDATA[31];
                end
            endcase
    end

    else begin
            spi_swrst         <= 1'b0;
            spi_rd            <= 1'b0;
            spi_wr            <= 1'b0;
            spi_qrd           <= 1'b0;
            spi_qwr           <= 1'b0;
            spi_clk_div_valid <= 1'b0;
        end     

end
//spi_reg
always @(posedge HCLK or negedge HRESETn) begin
    if (~HRESETn) begin
      spi_reg_wr_en <= 1'b0; 
    end
    else
        begin
        spi_reg_wr_en <= HTRANS == `HTRANS_NONSEQ && HWRITE && ~is_flash && is_IDLE;
        write_address_r <= write_address;
        end
end

//ensure cmd tx done;

wire [7:0] set_wrap_data;
assign set_wrap_data = {6'b0,wrap_bytes[1:0]};

always @(posedge HCLK or negedge HRESETn ) begin
    if (~HRESETn)
        prec_tx <= 1'b0;
    else if(is_PREC &&  prec_tx_valid)
        prec_tx <= 1'b0;
    else if(cmd_to_prec && cur_state == CMD)
        prec_tx<= 1'b1;

end
assign prec_to_cmd = is_PREC && is_spi_idle && !prec_tx;


//判断是否需要执行前置指令，wrap 是否有效，长度是否正确。

//CMD=>WAIT//
//no wrap OR wrap match
//cur_state == CMD
assign cmd_to_wait = cur_state == CMD && ~need_set_wrap;



//wait=>read
//cur_state == WAIT
//have a no respones req
assign wait_to_read = cur_state == WAIT && req_no_resp && ~req_is_write;
assign wait_to_write = cur_state == WAIT && req_no_resp && req_is_write;
//READ//
//response data
//replay data size:1B,2B,4B
//1Bor2B need extra operation
//data FIFO is 32bits width
//read_to_wait
//no end for burst && when replay data not AHB burst req

//read rx_fifo
reg [31:0] rdata_buf;
reg [3:0] rdata_valid;
reg fsm_rx_fifo_ready;
reg read_hready;
wire reply_1B,reply_2B,reply_4B;
assign reply_1B = data_size == 2'b00;
assign reply_2B = data_size == 2'b01;
assign reply_4B = data_size == 2'b10;

wire[31:0] spi_rx_data_to_little_end;

assign spi_rx_data_to_little_end =  is_burst || reply_4B ? {spi_data_rx[7:0],spi_data_rx[15:8],spi_data_rx[23:16],spi_data_rx[31:24]}:
                                                reply_2B ? {2{spi_data_rx[7:0],spi_data_rx[15:8]}}: spi_data_rx;



always @(posedge HCLK or negedge HRESETn ) begin
    if ( ~HRESETn) begin
      rdata_valid <= 4'b0;
    end
    else if (cur_state == READ && spi_data_rx_valid && spi_data_rx_ready)begin

      rdata_buf <= spi_rx_data_to_little_end ;
      rdata_valid <= is_burst || reply_4B ? 4'b1111 : 
                                 reply_2B ? 4'b0011 :4'b0001 ;
    end
    else if(cur_state == READ && rdata_valid != 4'b0 && req_no_resp) begin
        req_no_resp <= 1'b0;
        rdata_valid <=    reply_1B ? {1'b0,rdata_valid[3:1]} :
                        reply_2B ? {2'b0,rdata_valid[3:2]} : 4'b0;
        rdata_buf <=    reply_1B ? {8'b0,rdata_buf[31:8] } : {16'b0,rdata_buf[31:16]}; 
        FSMRDATA <= reply_1B ? {4{rdata_buf[7:0]}}    :
                    reply_2B ? {2{rdata_buf[15:0]}}    : rdata_buf;

    end

end
wire is_READ;
assign is_READ = cur_state == READ;
always @(posedge HCLK or negedge HRESETn ) begin
    if ( ~HRESETn) begin
      fsm_rx_fifo_ready <= 1'b0;
    end
    else if (cur_state == READ && spi_data_rx_valid && spi_data_rx_ready)begin
           fsm_rx_fifo_ready <= 1'b0; 
    end
    //if_data no valid need read data from rx_data_fifo
    else if (cur_state == READ && rdata_valid == 4'b0) begin
      fsm_rx_fifo_ready <= 1'b1;
    end
    else 
        fsm_rx_fifo_ready <= 1'b0;

end

always @(posedge HCLK or negedge HRESETn) begin
    if(~HRESETn)begin
      read_hready <= 1'b0;
    end
    else if ( is_READ && rdata_valid != 4'b0  )begin
      read_hready <= 1'b1;
    end
    else begin 
        read_hready <= 1'b0; 
    end
end


//HREDY source
//IDLE;CMD;WAIT;READ;WRITE
wire wait_hready,write_hready;
assign wait_hready = is_WAIT && !req_no_resp;

assign HREADY = ~is_flash         ? is_IDLE && is_spi_idle :
                cur_state == READ ? read_hready : 
                cur_state == IDLE ? idle_hready : 
                cur_state == WAIT ? wait_hready :
                cur_state == WRITE? write_hready: 
                1'b0;


//READ to WAIT//
//For same fiexed len burst;INCR4(8)(16);WRAP4(8）(16)
//When reply data , req already arrive
wire end_of_fix_burst;
assign read_to_wait = ~end_of_fix_burst && is_burst &&  !req_is_write &&  read_hready && is_READ;





//READ to CMD//
//end of fixed len burst;single 
//When reply data , req already arrive

assign read_to_cmd = read_hready && req_valid && (end_of_fix_burst || ~is_burst) && is_READ;
// all of can spport burst are fix length burst
assign end_of_fix_burst = pos_len == burst_beats && is_burst;


//READ to IDLE//
//end of fixed len burst;single 
//When reply data , not req arrive
//Or other;
//
assign read_to_idle =  read_hready && !req_valid && is_READ && (end_of_fix_burst || ~is_burst);

////WRITE_STATE//////

//need little-end to big-end
wire [31:0] wfifo_data;
reg [31:0] wfifo_data_buf;
reg [3:0] wdata_valid;
reg fsm_tx_fifo_valid;

wire wr_1B,wr_2B,wr_4B;
assign wr_1B = data_size == 2'b00;
assign wr_2B = data_size == 2'b01;
assign wr_4B = data_size == 2'b10;

 wire[31:0] spi_tx_data_to_big_end;

assign spi_tx_data_to_big_end = wr_4B ? {HWDATA[7:0],HWDATA[15:8],HWDATA[23:16],HWDATA[31:24]}:
                                wr_2B ? {{HWDATA[7:0],HWDATA[15:8],wfifo_data_buf[31:24],wfifo_data_buf[23:16]}}: {HWDATA[7:0],wfifo_data_buf[31:8]};



always @(posedge HCLK or negedge HRESETn ) begin
    if ( ~HRESETn) begin
      wdata_valid <= 4'b0;
    end
    else if (is_WRITE && req_no_resp && (is_burst && wdata_valid != 4'hF || !is_burst)  )begin

      wfifo_data_buf <=  spi_tx_data_to_big_end;
      wdata_valid <=             wr_4B ? 4'b1111 : 
                                 wr_2B ? {2'b11,wdata_valid[3:2]} :{1'b1,wdata_buf[3:1]} ;
    req_no_resp <= 1'b0;
    end
    else if(is_WRITE && !req_no_resp && (!is_burst || is_burst && wdata_valid == 4'hF) && fsm_tx_fifo_valid && spi_data_tx_ready ) begin
        wdata_valid <= 4'h0;
    end

end
always @(posedge HCLK or negedge HRESETn ) begin
    if ( ~HRESETn) begin
      fsm_tx_fifo_valid <= 1'b0;
    end
    else if (is_WRITE && spi_data_tx_valid && spi_data_tx_ready )begin
           fsm_tx_fifo_valid <= 1'b0; 
    end
    //if_data no valid need read data from rx_data_fifo
    else if (is_WRITE && !req_no_resp && (!is_burst || is_burst && wdata_valid == 4'hF) ) begin
      fsm_tx_fifo_valid <= 1'b1;
    end
    else if(is_WRITE && fsm_tx_fifo_valid && spi_data_tx_ready ) 
        fsm_tx_fifo_valid <= 1'b0;
end

assign write_hready = is_WRITE && !req_no_resp && ( is_burst && wdata_valid != 4'hF || !is_burst && wdata_valid == 4'h0) ;

assign write_to_wait = ~end_of_fix_burst && is_burst && req_valid && HWRITE &&  write_hready ;
assign write_to_cmd = write_hready && req_valid  && (end_of_fix_burst || ~is_burst);
assign write_to_idle = !req_valid && is_WRITE && wait_hready && (end_of_fix_burst || ~is_burst) ;

    // always @(posedge HCLK or negedge HRESETn) begin
    //     if (HRESETn == 1'b0) begin
    //         spi_swrst         <= 1'b0;
    //         spi_rd            <= 1'b0;
    //         spi_wr            <= 1'b0;
    //         spi_qrd           <= 1'b0;
    //         spi_qwr           <= 1'b0;
    //         spi_clk_div_valid <= 1'b0;
    //         spi_clk_div       <= 'b0;
    //         spi_cmd           <= 'b0;
    //         spi_addr          <= 'b0;
    //         spi_cmd_len       <= 'b0;
    //         spi_addr_len      <= 'b0;
    //         spi_data_len      <= 'b0;
    //         spi_dummy_rd      <= 'b0;
    //         spi_dummy_wr      <= 'b0;
    //         spi_csreg         <= 'b0;
    //         spi_int_th_tx     <= 'b0;
    //         spi_int_th_rx     <= 'b0;
    //         spi_int_en        <= 1'b0;
	// end else if (PSEL && PENABLE && PWRITE) begin
    //         spi_swrst         <= 1'b0;
    //         spi_rd            <= 1'b0;
    //         spi_wr            <= 1'b0;
    //         spi_qrd           <= 1'b0;
    //         spi_qwr           <= 1'b0;
    //         spi_clk_div_valid <= 1'b0;

    //         case (write_address)
    //             `REG_STATUS: begin
    //                 spi_rd    <= PWDATA[0];
    //                 spi_wr    <= PWDATA[1];
    //                 spi_qrd   <= PWDATA[2];
    //                 spi_qwr   <= PWDATA[3];
    //                 spi_swrst <= PWDATA[4];
    //                 spi_csreg <= PWDATA[11:8];
    //             end
    //             `REG_CLKDIV: begin
    //                 spi_clk_div       <= PWDATA[7:0];
    //                 spi_clk_div_valid <= 1'b1;
    //             end
    //             `REG_SPICMD: spi_cmd  <= PWDATA;
    //             `REG_SPIADR: spi_addr <= PWDATA;
    //             `REG_SPILEN: begin
    //                 spi_cmd_len        <= PWDATA[5:0];
    //                 spi_addr_len       <= PWDATA[13:8];
    //                 spi_data_len[7:0]  <= PWDATA[23:16];
    //                 spi_data_len[15:8] <= PWDATA[31:24];
    //             end
    //             `REG_SPIDUM: begin
    //                 spi_dummy_rd[7:0]  <= PWDATA[7:0];
    //                 spi_dummy_rd[15:8] <= PWDATA[15:8];
    //                 spi_dummy_wr[7:0]  <= PWDATA[23:16];
    //                 spi_dummy_wr[15:8] <= PWDATA[31:24];
    //             end
    //             `REG_INTCFG: begin
    //                 spi_int_th_tx <= PWDATA[LOG_BUFFER_DEPTH:0];
    //                 spi_int_th_rx <= PWDATA[8 + LOG_BUFFER_DEPTH:8];
    //                 spi_int_en    <= PWDATA[31];
    //             end
    //         endcase
	// end else begin
    //         spi_swrst         <= 1'b0;
    //         spi_rd            <= 1'b0;
    //         spi_wr            <= 1'b0;
    //         spi_qrd           <= 1'b0;
    //         spi_qwr           <= 1'b0;
    //         spi_clk_div_valid <= 1'b0;
    //     end
    // end  // SLAVE_REG_WRITE_PROC
    
    
    // implement slave model register read mux
reg [31:0] spi_reg_rdata;
    always @(*) begin
        HRDATA <= ~is_flash & ~is_READ ? spi_reg_rdata : FSMRDATA;
        case (read_address)
            `REG_STATUS: spi_reg_rdata = spi_status;
            `REG_CLKDIV: spi_reg_rdata = {24'h0, spi_clk_div};
            `REG_SPICMD: spi_reg_rdata = spi_cmd;
            `REG_SPIADR: spi_reg_rdata = spi_addr;
            `REG_SPILEN: spi_reg_rdata = {spi_data_len, 2'b00, spi_addr_len, 2'b00, spi_cmd_len};
            `REG_SPIDUM: spi_reg_rdata = {spi_dummy_wr, spi_dummy_rd};
            `REG_RXFIFO: spi_reg_rdata = spi_data_rx;
            `REG_INTCFG: begin
                spi_reg_rdata                         = 'b0;
                spi_reg_rdata[LOG_BUFFER_DEPTH:0]     = spi_int_th_tx;
                spi_reg_rdata[8 + LOG_BUFFER_DEPTH:8] = spi_int_th_rx;
                spi_reg_rdata[31]                     = spi_int_en;
            end
            `REG_INTSTA: spi_reg_rdata = spi_int_status;
            default: spi_reg_rdata = 'b0;
        endcase
    end    // SLAVE_REG_READ_PROC

    // assign spi_data_tx       = PWDATA;
    // assign spi_data_tx_valid = ((PSEL & PENABLE) & PWRITE) & (write_address == `REG_TXFIFO);
    // assign spi_data_rx_ready = ((PSEL & PENABLE) & ~PWRITE) & (read_address == `REG_RXFIFO);

//write data
//highest 8-bits is valid
    assign spi_data_tx       = ~is_flash && ~is_READ ? HWDATA  : is_WRITE ? wfifo_data_buf : {set_wrap_data,24'b0};
    assign spi_data_tx_valid = ~is_flash && !is_READ ? ((HTRANS == `HTRANS_NONSEQ ) & HWRITE) & (write_address == `REG_TXFIFO)  : is_WRITE ? fsm_tx_fifo_valid :prec_tx_valid;

    assign spi_data_rx_ready = ~is_flash && !is_READ ? ((HTRANS == `HTRANS_NONSEQ ) & !HWRITE) & (read_address == `REG_RXFIFO) :fsm_rx_fifo_ready;

endmodule
